<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0, minimal-ui" />
        
        <title>Documentation</title>
        
        <script src="/js/greyspots.js" type="text/javascript"></script>
        <script>
//global document, ml, GS, encodeHTML
//jslint browser:true
function sqlDialog() {
    "use strict";
    var strPostgreSQL = ml(function () {/*
DROP SCHEMA IF EXISTS gsdoc CASCADE;

CREATE SCHEMA gsdoc AUTHORIZATION postgres;

GRANT USAGE, CREATE ON SCHEMA gsdoc TO postgres;
GRANT USAGE ON SCHEMA gsdoc TO normal_g;
GRANT USAGE ON SCHEMA gsdoc TO public_g;

-- ####### gsdoc.default_stamp_fn(); #######
CREATE OR REPLACE FUNCTION gsdoc.default_stamp_fn()
  RETURNS trigger AS
$BODY$
DECLARE

BEGIN
  IF TG_OP = 'INSERT' THEN
    NEW.create_stamp := date_trunc('second',now());
  END IF;
  NEW.change_login := "session_user"();
  NEW.change_stamp := date_trunc('second',now());
  RETURN NEW;
END;

$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

ALTER FUNCTION gsdoc.default_stamp_fn() OWNER TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.default_stamp_fn() TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.default_stamp_fn() TO normal_g;


-- ####### gsdoc.global_seq; #######
CREATE SEQUENCE gsdoc.global_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 2316;

ALTER SEQUENCE gsdoc.global_seq OWNER TO postgres;
GRANT SELECT, UPDATE, USAGE ON TABLE gsdoc.global_seq TO postgres;
GRANT USAGE ON TABLE gsdoc.global_seq TO normal_g;


-- ####### gsdoc.rpeople; #######
CREATE TABLE gsdoc.rpeople(
  id integer NOT NULL DEFAULT nextval(('gsdoc.global_seq'::text)::regclass),
  first_name text NOT NULL,
  last_name text,
  birth_date timestamp with time zone,
  birth_time time,
  change_login name DEFAULT "current_user"(),
  change_stamp timestamp with time zone DEFAULT date_trunc('second'::text, ('now'::text)::timestamp with time zone),
  create_stamp timestamp with time zone DEFAULT date_trunc('second'::text, ('now'::text)::timestamp with time zone),
  CONSTRAINT rpeople_pk PRIMARY KEY (id)
) WITH (
  OIDS= FALSE
);

ALTER TABLE gsdoc.rpeople OWNER TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE,TRUNCATE,REFERENCES,TRIGGER ON TABLE gsdoc.rpeople TO postgres;


-- ####### ds_trg_rpeople ON gsdoc.rpeople; #######
CREATE TRIGGER ds_trg_rpeople
   BEFORE INSERT OR UPDATE
   ON gsdoc.rpeople
   FOR EACH ROW
   EXECUTE PROCEDURE gsdoc.default_stamp_fn();


-- ####### gsdoc.rpeople_line; #######
CREATE TABLE gsdoc.rpeople_line(
  id integer NOT NULL DEFAULT nextval(('gsdoc.global_seq'::text)::regclass),
  people_id integer NOT NULL,
  pet_name text NOT NULL,
  alive varchar(2) NOT NULL DEFAULT 0,
  birth_date timestamp with time zone NOT NULL,
  change_login name DEFAULT "current_user"(),
  change_stamp timestamp with time zone DEFAULT date_trunc('second'::text, ('now'::text)::timestamp with time zone),
  create_stamp timestamp with time zone DEFAULT date_trunc('second'::text, ('now'::text)::timestamp with time zone),
  CONSTRAINT rpeople_line_pk PRIMARY KEY (id)
) WITH (
  OIDS= FALSE
);

ALTER TABLE gsdoc.rpeople_line OWNER TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE,TRUNCATE,REFERENCES,TRIGGER ON TABLE gsdoc.rpeople_line TO postgres;


-- ####### ds_trg_rpeople_line ON gsdoc.rpeople_line; #######
CREATE TRIGGER ds_trg_rpeople_line
   BEFORE INSERT OR UPDATE
   ON gsdoc.rpeople_line
   FOR EACH ROW
   EXECUTE PROCEDURE gsdoc.default_stamp_fn();


-- ####### gsdoc.tpeople; #######
CREATE OR REPLACE VIEW gsdoc.tpeople AS
 SELECT rpeople.id, rpeople.first_name, rpeople.last_name, rpeople.birth_date, rpeople.birth_time,
    date_part('year'::text, age(rpeople.birth_date)) AS age, rpeople.change_stamp,
    count(rpeople_line.id) AS number_of_pets
   FROM gsdoc.rpeople
   LEFT JOIN gsdoc.rpeople_line ON rpeople_line.people_id = rpeople.id
  GROUP BY rpeople.id, rpeople.first_name, rpeople.last_name, rpeople.birth_date, rpeople.change_stamp;

ALTER TABLE gsdoc.tpeople OWNER TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE,TRUNCATE,REFERENCES,TRIGGER ON TABLE gsdoc.tpeople TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE ON TABLE gsdoc.tpeople TO normal_g;

-- ####### rpeople_delete ON gsdoc.tpeople; #######
CREATE OR REPLACE RULE rpeople_delete AS
    ON DELETE TO gsdoc.tpeople DO INSTEAD  DELETE FROM gsdoc.rpeople
  WHERE old.id = rpeople.id;

-- ####### rpeople_insert ON gsdoc.tpeople; #######
CREATE OR REPLACE RULE rpeople_insert AS
    ON INSERT TO gsdoc.tpeople DO INSTEAD  INSERT INTO gsdoc.rpeople (first_name, last_name, birth_date, birth_time)
  VALUES (new.first_name, new.last_name, new.birth_date, new.birth_time);

-- ####### rpeople_update ON gsdoc.tpeople; #######
CREATE OR REPLACE RULE rpeople_update AS
    ON UPDATE TO gsdoc.tpeople DO INSTEAD  UPDATE gsdoc.rpeople SET first_name = new.first_name, last_name = new.last_name, birth_date = new.birth_date, birth_time = new.birth_time
  WHERE old.id = rpeople.id;


-- ####### gsdoc.tpeople_list; #######
CREATE OR REPLACE VIEW gsdoc.tpeople_list AS
 SELECT rpeople.id, rpeople.first_name || ' ' || rpeople.last_name AS name, date_part('year'::text, age(rpeople.birth_date)) AS age
   FROM gsdoc.rpeople;

ALTER TABLE gsdoc.tpeople_list OWNER TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE,TRUNCATE,REFERENCES,TRIGGER ON TABLE gsdoc.tpeople_list TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE ON TABLE gsdoc.tpeople_list TO normal_g;


-- ####### gsdoc.tpeople_list_inital; #######
CREATE OR REPLACE VIEW gsdoc.tpeople_list_inital AS
 SELECT rpeople.id, rpeople.first_name || ' ' || rpeople.last_name AS name
   FROM gsdoc.rpeople;

ALTER TABLE gsdoc.tpeople_list_inital OWNER TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE,TRUNCATE,REFERENCES,TRIGGER ON TABLE gsdoc.tpeople_list_inital TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE ON TABLE gsdoc.tpeople_list_inital TO normal_g;


-- ####### gsdoc.tpeople_line; #######
CREATE OR REPLACE VIEW gsdoc.tpeople_line AS
 SELECT rpeople_line.id, rpeople_line.people_id, rpeople_line.pet_name, rpeople_line.alive, rpeople_line.birth_date,
    date_part('year'::text, age(rpeople_line.birth_date)) AS age, rpeople_line.change_stamp
   FROM gsdoc.rpeople_line;

ALTER TABLE gsdoc.tpeople_line OWNER TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE,TRUNCATE,REFERENCES,TRIGGER ON TABLE gsdoc.tpeople_line TO postgres;
GRANT SELECT,UPDATE,INSERT,DELETE ON TABLE gsdoc.tpeople_line TO normal_g;

-- ####### rpeople_line_delete ON gsdoc.tpeople_line; #######
CREATE OR REPLACE RULE rpeople_line_delete AS
    ON DELETE TO gsdoc.tpeople_line DO INSTEAD  DELETE FROM gsdoc.rpeople_line
  WHERE old.id = rpeople_line.id;

-- ####### rpeople_line_insert ON gsdoc.tpeople_line; #######
CREATE OR REPLACE RULE rpeople_line_insert AS
    ON INSERT TO gsdoc.tpeople_line DO INSTEAD  INSERT INTO gsdoc.rpeople_line (people_id, pet_name, birth_date)
  VALUES (new.people_id, new.pet_name, new.birth_date);

-- ####### rpeople_line_update ON gsdoc.tpeople_line; #######
CREATE OR REPLACE RULE rpeople_line_update AS
    ON UPDATE TO gsdoc.tpeople_line DO INSTEAD  UPDATE gsdoc.rpeople_line SET people_id = new.people_id, pet_name = new.pet_name, birth_date = new.birth_date
  WHERE old.id = rpeople_line.id;


-- ##########################################################
-- ################# RANDOM DATA GENERATION #################
-- ##########################################################

-- generate 1 person with the first name of "Apple" so that when testing where clauses based on first name: we can count on "Apple"
INSERT INTO gsdoc.rpeople
    SELECT nextval(('gsdoc.global_seq'::text)::regclass) AS id,
           'Apple',
           arr_names[trunc(random() * 19 + 1)] AS last_name,
           now() - (trunc(random() * 10000 + 1) || ' day')::interval AS birth_date
     FROM (SELECT ARRAY['Apple', 'Mango', 'Kiwi', 'Pineapple',
                        'Grapefruit', 'Plum', 'Coconut', 'Fig',
                        'Lemon', 'Grapes', 'Raspberries', 'Blackberries',
                        'Blueberries', 'Strawberries', 'Cranberries', 'Orange',
                        'Banana', 'Lime', 'Pear'] AS arr_names) em;

-- generate 100 random people
INSERT INTO gsdoc.rpeople
    SELECT nextval(('gsdoc.global_seq'::text)::regclass) AS id,
           arr_names[trunc(random() * 19 + 1)] AS first_name,
           arr_names[trunc(random() * 19 + 1)] AS last_name,
           now() - (trunc(random() * 10000 + 1) || ' day')::interval AS birth_date,
           now()::time - (trunc(random() * 43200 + 1) || ' seconds')::interval AS birth_time
     FROM (SELECT generate_series(1,100) AS series_maker,
            ARRAY['Apple', 'Mango', 'Kiwi', 'Pineapple',
                  'Grapefruit', 'Plum', 'Coconut', 'Fig',
                  'Lemon', 'Grapes', 'Raspberries', 'Blackberries',
                  'Blueberries', 'Strawberries', 'Cranberries', 'Orange',
                  'Banana', 'Lime', 'Pear'] AS arr_names) em;

-- generate 120 random pets and assign them to random people
INSERT INTO gsdoc.rpeople_line
    SELECT nextval(('gsdoc.global_seq'::text)::regclass) AS id,
           (SELECT id FROM gsdoc.rpeople LIMIT 1 OFFSET random_offset) AS people_id,
           arr_names[trunc(random() * 13 + 1)] AS pet_name,
           CASE WHEN random() < 0.5 THEN '0' ELSE '-1' END AS alive,
           now() - (trunc(random() * 5000 + 1) || ' day')::interval AS birth_date
     FROM (SELECT generate_series(1,120) AS series_maker,
            trunc(random() * 99 + 1) random_offset,
            ARRAY['Spot', 'Roger', 'Cookie', 'Tracer',
                  'Tracy', 'Fido', 'King', 'Queen',
                  'Hound', 'Shepherd', 'Wolfy', 'Max',
                  'Bear'] AS arr_names) em;

-- generate 3 random pets and assign them to person #2341 so that we can depend on there being line items
INSERT INTO gsdoc.rpeople_line
    SELECT nextval(('gsdoc.global_seq'::text)::regclass) AS id,
           2341 AS people_id,
           arr_names[trunc(random() * 13 + 1)] AS pet_name,
           CASE WHEN random() < 0.5 THEN '0' ELSE '-1' END AS alive,
           now() - (trunc(random() * 5000 + 1) || ' day')::interval AS birth_date
     FROM (SELECT generate_series(1,3) AS series_maker,
            ARRAY['Spot', 'Roger', 'Cookie', 'Tracer',
                  'Tracy', 'Fido', 'King', 'Queen',
                  'Hound', 'Shepherd', 'Wolfy', 'Max',
                  'Bear'] AS arr_names) em;

CREATE OR REPLACE VIEW gsdoc.tenv_example AS
    SELECT id, first_name, last_name, change_stamp
    FROM gsdoc.rpeople;

GRANT SELECT,UPDATE,INSERT,DELETE ON
    TABLE gsdoc.tenv_example TO developer_g;

CREATE OR REPLACE RULE tenv_example_insert AS
    ON INSERT TO gsdoc.tenv_example DO INSTEAD
        INSERT INTO gsdoc.rpeople (first_name, last_name)
            VALUES (new.first_name, new.last_name);

CREATE OR REPLACE RULE tenv_example_update AS
    ON UPDATE TO gsdoc.tenv_example DO INSTEAD
        UPDATE gsdoc.rpeople SET first_name = new.first_name,
            last_name = new.last_name
            WHERE old.id = rpeople.id;

CREATE OR REPLACE RULE tenv_example_delete AS
    ON DELETE TO gsdoc.tenv_example DO INSTEAD
        DELETE FROM gsdoc.rpeople WHERE old.id = rpeople.id;

CREATE OR REPLACE VIEW gsdoc.texample AS
 SELECT id, first_name, last_name, change_stamp
 FROM gsdoc.rpeople;

GRANT SELECT,UPDATE,INSERT,DELETE ON 
    TABLE gsdoc.texample TO developer_g;

CREATE OR REPLACE RULE texample_insert AS
    ON INSERT TO gsdoc.texample DO INSTEAD  
        INSERT INTO gsdoc.rpeople
            (first_name, last_name) 
            VALUES (new.first_name, new.last_name);

CREATE OR REPLACE RULE texample_delete AS
    ON DELETE TO gsdoc.texample DO INSTEAD
        DELETE FROM gsdoc.rpeople WHERE old.id = rpeople.id;
        
CREATE OR REPLACE RULE texample_update AS
    ON UPDATE TO gsdoc.texample DO INSTEAD  
        UPDATE gsdoc.rpeople
            SET first_name = new.first_name, 
                last_name = new.last_name
         WHERE old.id = rpeople.id;

CREATE OR REPLACE FUNCTION gsdoc.text_to_uri(text)
  RETURNS text AS
$BODY$
DECLARE
  str_working text;
  str_slice text;
  str_ret text;

BEGIN
  str_working := $1;
  str_ret := '';
  WHILE length(str_working) > 0 LOOP
    str_slice := substring(str_working, 1, 1);
    IF str_slice ~* '[a-z]|[0-9]' THEN
      str_ret := str_ret || str_slice;
    ELSE
      str_ret := str_ret || '%' || encode(str_slice::bytea,'hex');
    END IF;
    str_working := substring(str_working, 2);
  END LOOP;

  RETURN str_ret;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION gsdoc.text_to_uri(text)
  OWNER TO postgres;

-- Function: gsdoc.getpar(text, text)

-- DROP FUNCTION gsdoc.getpar(text, text);

CREATE OR REPLACE FUNCTION gsdoc.getpar(text, text)
  RETURNS text AS
$BODY$
DECLARE
  strArray text[];
  ret text;
  -- accepts encoded uri ( key=value, key )

BEGIN
  strArray := string_to_array($1,'&');
  IF array_upper(strArray,1) IS NULL THEN
    RETURN NULL;
  ELSE
    for i IN 1..array_upper(strArray,1) loop
      IF split_part(strArray[i],'=',1) = $2 THEN
         ret = substring(strArray[i] FROM position('=' in strArray[i]) + 1);
      END IF;
    end loop;
    IF ret != '' THEN
      ret := gsdoc.uri_to_text(ret);
    ELSE
      ret := NULL;
    END IF;
    RETURN ret;
  END IF;
END

$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION gsdoc.getpar(text, text) OWNER TO postgres;

-- DROP FUNCTION gsdoc.jsonify(anyelement);

CREATE OR REPLACE FUNCTION gsdoc.jsonify(anyelement)
  RETURNS text AS
$BODY$
DECLARE
BEGIN
  RETURN rtrim(ltrim(array_to_json(ARRAY[[$1]])::text, '['), ']');
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

ALTER FUNCTION gsdoc.jsonify(anyelement) OWNER TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.jsonify(anyelement) TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.jsonify(anyelement) TO public;;

--SELECT gsdoc.jsonify(anyelement);

-- Function: gsdoc.uri_to_text(text)

-- DROP FUNCTION gsdoc.uri_to_text(text);

CREATE OR REPLACE FUNCTION gsdoc.uri_to_text(text)
  RETURNS text AS
$BODY$
DECLARE
  str_working text;
  str_slice text;
  arr_working text[];
  str_ret text;

BEGIN
  str_working := replace($1,'+',' ');
  arr_working := regexp_split_to_array(str_working, E'%');
  FOR i IN 2 .. array_upper(arr_working,1) LOOP
    str_slice := substring(arr_working[i] from 1 for 2);
    IF str_slice < '2' OR str_slice > '7E' THEN
      IF str_slice ilike '0D' THEN
        arr_working[i] := chr(13) ||- substring(arr_working[i] from 3);
      ELSE
        arr_working[i] := substring(arr_working[i] from 3);
      END IF;
    ELSE
      arr_working[i] := convert_from(decode(substring(arr_working[i] from 1 for 2),'hex'),'UTF8') ||- substring(arr_working[i] from 3);
    END IF;
  END LOOP;
  str_ret := array_to_string(arr_working, ''::text)::text;
--SELECT decode('20','hex') => creates bytea return type, need convert_from to fix
--notes from php that helped me build this:
--$str =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
--Now, the reverse, decode a string from a url. Equivalent of urldecode in PHP.
--$str =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;

  RETURN str_ret;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION gsdoc.uri_to_text(text) OWNER TO postgres;

CREATE OR REPLACE FUNCTION gsdoc.action_test(str_args text)
    RETURNS text AS
$BODY$
DECLARE
    str_test text;
BEGIN
    --get the "test" parameter
    str_test := gsdoc.getpar(str_args, 'test');
    --jsonify the return variable
    RETURN gsdoc.jsonify(str_test);
    --try all of these
    --RETURN gsdoc.jsonify(current_setting('sunny.context_info'));
    --RETURN '123';
    --RETURN array_to_json(ARRAY[[str_args]]::text[][]);
    --RETURN '"test"';
    --RETURN '{"test": "test", "test2": 123}';
END;

$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.action_test(text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.action_test(text) TO normal_g;
REVOKE ALL ON FUNCTION gsdoc.action_test(text) FROM public;

CREATE OR REPLACE FUNCTION gsdoc.accept_test(str_args text)
    RETURNS text AS
$BODY$
BEGIN
    RETURN E'HTTP/1.1 200 OK\r\n' ||
        E'Content-Type: text/csv\r\n\r\n' ||
        E'Header1, Header2, Header3\r\n' ||
        E'Data11, Data12, Data13\r\n' ||
        E'Data21, Data22, Data23\r\n' ||
        E'Data31, Data32, ' || str_args || '\r\n';
END;

$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.accept_test(text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.accept_test(text) TO normal_g;
REVOKE ALL ON FUNCTION gsdoc.accept_test(text) FROM public;

CREATE OR REPLACE FUNCTION gsdoc.acceptnc_test(str_args text)
  RETURNS text AS
$BODY$
BEGIN
    RETURN E'HTTP/1.1 200 OK\r\n' ||
        E'Content-Type: text/csv\r\n\r\n' ||
        E'Header1, Header2, Header3\r\n' ||
        E'Data11, Data12, Data13\r\n' ||
        E'Data21, Data22, Data23\r\n' ||
        E'Data31, Data32, ' || str_args || '\r\n';
END;

$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

ALTER FUNCTION gsdoc.acceptnc_test(str_args text) OWNER TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.acceptnc_test(str_args text) TO public_g;
GRANT EXECUTE ON FUNCTION gsdoc.acceptnc_test(str_args text) TO postgres;
REVOKE ALL ON FUNCTION gsdoc.acceptnc_test(str_args text) FROM public;

CREATE OR REPLACE FUNCTION gsdoc.actionnc_test(str_args text)
  RETURNS text AS
$BODY$
BEGIN
    --You can get the ip address and host fields from the request. Since you don't know who logged in you can use these headers to determine who is who, but not as reliably as a cookie. This also available on cookie requests, but is not as useful.
    RETURN
        '{"context_info": "' || current_setting('sunny.context_info') || '"}';
    --try all of these
    --RETURN '123';
    --RETURN array_to_json(ARRAY[[str_args]]::text[][]);
    --RETURN '"test"';
    --RETURN '{"test": "test", "test2": 123}';
END;

$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

ALTER FUNCTION gsdoc.actionnc_test(str_args text) OWNER TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.actionnc_test(str_args text) TO postgres;
GRANT EXECUTE ON FUNCTION gsdoc.actionnc_test(str_args text) TO public_g;
REVOKE ALL ON FUNCTION gsdoc.actionnc_test(str_args text) FROM public;

CREATE OR REPLACE FUNCTION gsdoc.action_ajax_test(str_args text)
    RETURNS text AS
$BODY$
BEGIN
    RETURN '{"asdf": "test"}';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.action_ajax_test(str_args text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.action_ajax_test(str_args text) TO normal_g;

CREATE OR REPLACE FUNCTION gsdoc.action_ajax_test(str_args text)
    RETURNS text AS
$BODY$
BEGIN
    RETURN '{"asdf": "test"}';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.action_ajax_test(str_args text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.action_ajax_test(str_args text) TO normal_g;

CREATE OR REPLACE FUNCTION gsdoc.action_ajax_test_json(text)
    RETURNS text AS
$BODY$
BEGIN
    RETURN '"' || $1 || '"';
    --RETURN '{"test": "' || $1 || '"}';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.action_ajax_test_json(text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.action_ajax_test_json(text) TO normal_g;

CREATE OR REPLACE FUNCTION gsdoc.accept_ajax_test(text)
    RETURNS text AS
$BODY$
BEGIN
    RETURN E'HTTP/1.1 200 OK\r\n' ||
        E'Content-Type: text/plain\r\n\r\n' ||
        E'This is some test text.\r\n';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.accept_ajax_test(text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.accept_ajax_test(text) TO normal_g;

CREATE OR REPLACE FUNCTION gsdoc.action_ajax_test_error(text)
    RETURNS text AS
$BODY$
BEGIN
    RETURN 'This is a piece of text... Hello world...' || nonexistentschema.function(); -- sql error here
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.action_ajax_test_error(text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.action_ajax_test_error(text) TO normal_g;

CREATE OR REPLACE FUNCTION gsdoc.action_ajax_test_timeout(text)
    RETURNS text AS
$BODY$
BEGIN
    SELECT pg_sleep(10);
    RETURN 'test';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION gsdoc.action_ajax_test_timeout(text) OWNER TO postgres;
GRANT ALL ON FUNCTION gsdoc.action_ajax_test_timeout(text) TO normal_g;
*/
    });

    strPostgreSQL = encodeHTML(strPostgreSQL);

    var templateElement = document.createElement("template");

    templateElement.setAttribute("data-overlay-close", "true");
    templateElement.setAttribute("data-mode", "constrained");
    templateElement.innerHTML = ml(function () {/*
        <gs-page>
            <gs-header>
                <center><h3>Requirements for Documentation</h3></center>
            </gs-header>
            <gs-body flex-vertical flex-fill padded>
                <div>
                    Some examples in the documentation require a database object. All
                    of the database objects the documentation needs need to be in a
                    schema called "gsdoc". Below we've included the code necessary to
                    create the "gsdoc" schema, please run the code below in your
                    database.
                </div>
                <div class="text-right">
                    <small><gs-button id="button-select-all" inline remove-bottom>Copy Code</gs-button></small>
                </div>
                <textarea id="area-sql-text" style="resize: none;" flex>{{SQL}}</textarea>
            </gs-body>
            <gs-footer>
                <gs-button dialogclose>Done</gs-button>
            </gs-footer>
        </gs-page>*/
    }).replace(/\{\{SQL\}\}/gi, strPostgreSQL);

    function copyStringToClipboard (string) {
        function handler (event){
            event.clipboardData.setData('text/plain', string);
            event.preventDefault();
            document.removeEventListener('copy', handler, true);
        }
    
        document.addEventListener('copy', handler, true);
        document.execCommand('copy');
    }


    GS.openDialog(templateElement, function () {
        document.getElementById('button-select-all').addEventListener('click', function () {
            var sqlArea = document.getElementById('area-sql-text');
            sqlArea.focus();
            GS.setInputSelection(sqlArea, 0, sqlArea.value.length);
            copyStringToClipboard(sqlArea.value);
        });
    });
}
        </script>
        <link href="/css/greyspots.css" type="text/css" rel="stylesheet" />
    </head>
    <body>
        <gs-jumbo>
            <h1 class="text-center">Documentation</h1>
        </gs-jumbo>
        
        <center>
            <gs-button inline onclick="sqlDialog();">Requirements for Documentation</gs-button>
        </center>
        
        <gs-container min-width="sml;med;lrg" padded>
            <gs-grid gutter reflow-at="768px">
                <gs-block>
                    <center><h3>Layout</h3></center>
                    <gs-button href="doc-elem-container-jumbo.html">Container/Jumbo</gs-button>
                    <gs-button href="doc-jslib-dialog.html">Dialog</gs-button>
                    <gs-button href="doc-elem-font.html">Font Size</gs-button>
                    <gs-button href="doc-elem-grid.html">Grid</gs-button>
                    <gs-button href="doc-elem-group.html">Group</gs-button>
                    <gs-button href="doc-elem-img.html">Image</gs-button>
                    <gs-button href="doc-elem-page.html">Page</gs-button>
                    <gs-button href="doc-elem-panel.html">Panel</gs-button>
                    <gs-button href="doc-jslib-pushmessage.html">Push Message</gs-button>
                    <gs-button href="doc-elem-scroller.html">Scroller</gs-button>
                    <gs-button href="doc-elem-sticky.html">Sticky Header/Footer</gs-button>
                    <gs-button href="doc-elem-switch.html">Switch</gs-button>
                    <gs-button href="doc-elem-typography.html">Typography</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Control</h3></center>
                    <gs-button href="doc-elem-buttons-toggle.html">Buttons And Toggle</gs-button>
                    <gs-button href="doc-elem-dialog-button.html">Dialog Button</gs-button>
                    <gs-button href="doc-elem-checkbox.html">Checkbox</gs-button>
                    <gs-button href="doc-elem-combo.html">Combo</gs-button>
                    <gs-button href="doc-elem-datetime.html">Datetime</gs-button>
                    <gs-button href="doc-elem-date.html">Date</gs-button>
                    <gs-button href="doc-elem-interval.html">Interval</gs-button>
                    <gs-button href="doc-elem-listbox.html">Listbox</gs-button>
                    <gs-button href="doc-elem-memo.html">Memo</gs-button>
                    <gs-button href="doc-elem-number.html">Number</gs-button>
                    <gs-button href="doc-elem-optionbox.html">OptionBox</gs-button>
                    <gs-button href="doc-elem-search.html">Search</gs-button>
                    <gs-button href="doc-elem-select.html">Select</gs-button>
                    <gs-button href="doc-elem-static.html">Static</gs-button>
                    <gs-button href="doc-elem-text.html">Text</gs-button>
                    <gs-button href="doc-elem-folder.html">Folder Manager</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Record</h3></center>
                    <gs-button href="doc-elem-delete-button.html">Delete Button</gs-button>
                    <gs-button href="doc-elem-envelope.html">Envelope</gs-button>
                    <gs-button href="doc-elem-form.html">Form</gs-button>
                    <gs-button href="doc-elem-insert.html">Insert</gs-button>
                    <gs-button href="doc-elem-datasheet.html">Datasheet</gs-button>
                </gs-block>
            </gs-grid>
            
            <gs-grid gutter reflow-at="768px">
                <gs-block>
                    <center><h3>Utility Attributes</h3></center>
                    <gs-button href="doc-attr-flex.html">CSS Flexbox</gs-button>
                    <gs-button href="doc-attr-visibility.html">Visibility</gs-button>
                    <gs-button href="doc-attr-text-selection.html">Text Selection</gs-button>
                    <gs-button href="doc-attr-text-insertion.html">Text Insertion Attributes For Mobile Devices</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Ajax Functions</h3></center>
                    <gs-button href="doc-jslib-loader.html">Loader</gs-button>
                    <gs-button href="doc-jslib-standard-ajax.html">Standard Ajax</gs-button>
                    <gs-button href="doc-jslib-duplicate-safe-ajax.html">Duplicate-safe Ajax</gs-button>
                    <gs-button href="doc-jslib-ajax-error-dialog.html">Ajax Error Dialog</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Text Functions</h3></center>
                    <gs-button href="doc-jslib-padding.html">String Padding</gs-button>
                    <gs-button href="doc-jslib-str-to-title.html">String To Title</gs-button>
                    <gs-button href="doc-jslib-safe-uri-decode.html">Safe URI Decoding</gs-button>
                    <gs-button href="doc-jslib-get-text-height.html">Text Height Calculation</gs-button>
                    <gs-button href="doc-jslib-trim.html">String Trimming</gs-button>
                    <gs-button href="doc-jslib-search-to-where.html">Where Clause Generation</gs-button>
                    <gs-button href="doc-jslib-lorem-text.html">Lorem Ipsum Text</gs-button>
                    <gs-button href="doc-jslib-query-string.html">Query String Functions</gs-button>
                </gs-block>
            </gs-grid>
            
            <gs-grid gutter reflow-at="768px">
                <gs-block>
                    <center><h3>Event Functions</h3></center>
                    <gs-button href="doc-jslib-trigger-event.html">Trigger Event</gs-button>
                    <gs-button href="doc-jslib-keycode-functions.html">keyCode Utility Functions</gs-button>
                    <gs-button href="doc-jslib-mouse-position.html">Mouse Position Normalizer</gs-button>
                    <gs-button href="doc-jslib-beforeunload.html">Add Before Unload Event</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Element Functions</h3></center>
                    <gs-button href="doc-jslib-parent-finding.html">Find Parent</gs-button>
                    <gs-button href="doc-jslib-insert-element-after.html">Insert Element After</gs-button>
                    <gs-button href="doc-jslib-animate-style.html">Animate Style</gs-button>
                    <gs-button href="doc-jslib-string-to-element.html">Create Element From String</gs-button>
                    <gs-button href="doc-jslib-clone-element.html">Clone Element</gs-button>
                    <gs-button href="doc-jslib-is-element-focusable.html">Is Element Focusable</gs-button>
                    <gs-button href="doc-jslib-scrolling.html">Scrolling Functions</gs-button>
                    <gs-button href="doc-jslib-input-selection.html">Input Selection</gs-button>
                    <gs-button href="doc-jslib-element-position.html">Element Position Functions</gs-button>
                    <gs-button href="doc-jslib-get-style.html">Get Style</gs-button>
                    <gs-button href="doc-jslib-make-table-selectable.html">Make Table Selectable</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Utility Functions</h3></center>
                    <gs-button href="doc-jslib-url-state.html">URL State Functions</gs-button>
                    <gs-button href="doc-jslib-px-em-conversion.html">Pixel and EM conversion</gs-button>
                    <gs-button href="doc-jslib-list-add.html">Add Unique To Array</gs-button>
                    <gs-button href="doc-jslib-guid.html">GUID</gs-button>
                    <gs-button href="doc-jslib-cookie.html">Cookie Functions</gs-button>
                    <gs-button href="doc-jslib-account.html">Account Functions</gs-button>
                    <gs-button href="doc-jslib-version.html">GS Library Version</gs-button>
                    <gs-button href="doc-jslib-icon-list.html">Icon List</gs-button>
                    <gs-button href="doc-jslib-show-shimmed.html">Show Shimmed</gs-button>
                </gs-block>
            </gs-grid>
            
            <gs-grid gutter reflow-at="768px">
                <gs-block>
                    <center><h3>Ajax API</h3></center>
                    <gs-button href="doc-api-auth.html">Authentication Actions</gs-button>
                    <gs-button href="doc-api-database.html">Database Actions</gs-button>
                    <gs-button href="doc-api-public.html">Public Actions</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>WebSocket API</h3></center>
                    <gs-button href="doc-jslib-websocket.html">WebSocket Functions</gs-button>
                    <gs-button href="doc-api-file.html">File Actions</gs-button>
                </gs-block>
                <gs-block>
                    <center><h3>Misc</h3></center>
                    <gs-button href="doc-lib-script-so.html">Script.so</gs-button>
                    <gs-button href="doc-permissions.html">Permissions</gs-button>
                    <gs-button href="doc-template.html">doT.js</gs-button>
                </gs-block>
            </gs-grid>
        </gs-container>
    </body>
</html>